All files / web/src/app/api/classrooms/[classroomId]/presence/active-sessions route.ts

0% Statements 0/108
0% Branches 0/1
0% Functions 0/1
0% Lines 0/108

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109                                                                                                                                                                                                                         
import { and, eq, inArray, isNull } from 'drizzle-orm'
import { NextResponse } from 'next/server'
import { db, schema } from '@/db'
import { getClassroomPresence, getEnrolledStudents, getTeacherClassroom } from '@/lib/classroom'
import { getUserId } from '@/lib/viewer'
import { withAuth } from '@/lib/auth/withAuth'

/**
 * Active session information returned by this endpoint
 */
interface ActiveSessionInfo {
  /** Session plan ID (for observation) */
  sessionId: string
  /** Player ID */
  playerId: string
  /** When the session started */
  startedAt: Date
  /** Current part index */
  currentPartIndex: number
  /** Current slot index within the part */
  currentSlotIndex: number
  /** Total parts in session */
  totalParts: number
  /** Total problems in session (sum of all slots) */
  totalProblems: number
  /** Number of completed problems */
  completedProblems: number
  /** Whether the student is currently present in the classroom */
  isPresent: boolean
}

/**
 * GET /api/classrooms/[classroomId]/presence/active-sessions
 * Get active practice sessions for enrolled students in the classroom
 *
 * Returns: { sessions: ActiveSessionInfo[] }
 *
 * This endpoint allows teachers to see which students are actively practicing.
 * It returns sessions for ALL enrolled students, not just present ones.
 * The `isPresent` field indicates whether the teacher can observe the session.
 */
export const GET = withAuth(async (_request, { params }) => {
  try {
    const { classroomId } = (await params) as { classroomId: string }
    const userId = await getUserId()

    // Verify user is the teacher of this classroom
    const classroom = await getTeacherClassroom(userId)
    if (!classroom || classroom.id !== classroomId) {
      return NextResponse.json({ error: 'Not authorized' }, { status: 403 })
    }

    // Get all enrolled students in the classroom
    const enrolledStudents = await getEnrolledStudents(classroomId)
    const enrolledPlayerIds = enrolledStudents.map((s) => s.id)

    if (enrolledPlayerIds.length === 0) {
      return NextResponse.json({ sessions: [] })
    }

    // Get presence info to know which students are present
    const presences = await getClassroomPresence(classroomId)
    const presentPlayerIds = new Set(
      presences.filter((p) => p.player !== undefined).map((p) => p.player!.id)
    )

    // Find active sessions for enrolled students
    // Active = status is 'in_progress', startedAt is set, completedAt is null
    const activeSessions = await db.query.sessionPlans.findMany({
      where: and(
        inArray(schema.sessionPlans.playerId, enrolledPlayerIds),
        eq(schema.sessionPlans.status, 'in_progress'),
        isNull(schema.sessionPlans.completedAt)
      ),
    })

    // Map to ActiveSessionInfo
    const sessions: ActiveSessionInfo[] = activeSessions
      .filter((session) => session.startedAt)
      .map((session) => {
        // Calculate total and completed problems
        const parts = session.parts
        const totalProblems = parts.reduce((sum, part) => sum + part.slots.length, 0)
        let completedProblems = 0
        for (let i = 0; i < session.currentPartIndex; i++) {
          completedProblems += parts[i].slots.length
        }
        completedProblems += session.currentSlotIndex

        return {
          sessionId: session.id,
          playerId: session.playerId,
          startedAt: session.startedAt as Date,
          currentPartIndex: session.currentPartIndex,
          currentSlotIndex: session.currentSlotIndex,
          totalParts: parts.length,
          totalProblems,
          completedProblems,
          isPresent: presentPlayerIds.has(session.playerId),
        }
      })

    return NextResponse.json({ sessions })
  } catch (error) {
    console.error('Failed to fetch active sessions:', error)
    return NextResponse.json({ error: 'Failed to fetch active sessions' }, { status: 500 })
  }
})